import asyncio
import time

"""
python 异步：https://zhuanlan.zhihu.com/p/698683843
携程：https://zhuanlan.zhihu.com/p/172471249
"""

####################################### 携程
"""
pyton中携程的有两层含义：
1. 使用 async def 定义的函数是一个coroutine,这个函数内部可以用await关键字（反过来说，如果没有被async修饰的函数内部就不能使用await。否则就抛出SyntaxError语法错误）。
2. 使用 async def 定义的函数，调用之后返回的值，是一个coroutine对象（coroutine是一个python的内置子对象）可以被用于await或者asyncio.run,
即：await 后面跟着的可以是coroutine对象
"""


async def coroutine_function():
    await asyncio.sleep(2)
    return "this is coroutine function.."


coro = coroutine_function()
# <function coroutine_function at 0x00000145B55807C0>
print(coroutine_function)
# <coroutine object coroutine_function at 0x0000027769CC9C00>
print(coro)
# 输出<class 'coroutine'>
print(coro.__class__)
print("end..")

print("-" * 40)


####################################### await + coroutine

"""
当我们对一个coroutine对象使用await时（比如：await asyncio.sleep(1)），当前函数终端执行，python解释器开始执行coroutine的代码，这和普通的函数调用没什么区别。
"""

async def async_hello_world():
    now = time.time()
    # asyncio.sleep(1)本身也是使用async修饰的，因此：asyncio.sleep(1)返回的是携程对象，可以被await修饰
    await asyncio.sleep(1)
    print("first:", time.time() - now)
    print("hello,world")
    # asyncio.sleep(1)本身也是使用async修饰的，因此：asyncio.sleep(1)返回的是携程对象，可以被await修饰
    await asyncio.sleep(1)
    print("second:", time.time() - now)


async def main():
    await asyncio.gather(async_hello_world(), async_hello_world(), async_hello_world())


now = time.time()
asyncio.run(main())
# 差不多2秒左右
print(f"运行3次携程耗时:{time.time() - now}秒")

print("-" * 10)


def normal_hello_world():
    now = time.time()
    time.sleep(1)
    print("first:", time.time() - now)
    print("hello,world")
    time.sleep(1)
    print("second:", time.time() - now)

now = time.time()
normal_hello_world()
normal_hello_world()
normal_hello_world()
# 差不多6秒
print(f"运行3次普通函数耗时:{time.time() - now}秒")

print("-" * 40)
####################################### await + task

"""
在python的异步编程中，真正并发的对象是任务（task）。当我们对一个task进行await的时候，event loop开始调度当前可执行的全部任务，直到被await的task结束。
"""

# 使用task来模拟asyncio.gather的效果（事实上，asyncio.gather就是使用task实现的）

async def async_hello_world():
    now = time.time()
    await asyncio.sleep(1)
    print("first:", time.time() - now)
    print("hello,world")
    await asyncio.sleep(1)
    print("second:", time.time() - now)

async def main():
    """
    asyncio.create_task(async_hello_world()):
    1:      async_hello_world(): 创建了coroutine对象。
    2.asyncio.create_task(coro): 将coroutine包装成了task，并放入到event loop（事件循环）中去执行。然后返回task。
    task可以理解为是event loop的调度单元，用于跟踪协程的执行状态和结果

    await 当前协程等待 对应task完成，实际上task是由event loop驱动执行的！是异步执行的！

    event loop：只接受task，task是event loop的调度单元。

    await 说明了两件事情
    1. 当前协程等到await后面的协程方法结束，才会继续执行下面的代码。
    2. await关键字相当于告知event loop：你去干其他事情吧，不需要管我了吗，我让出了事件调度。
    :return:
    """
    task1 = asyncio.create_task(async_hello_world())
    task2 = asyncio.create_task(async_hello_world())
    task3 = asyncio.create_task(async_hello_world())
    await task1
    await task2
    await task3


now = time.time()
asyncio.run(main())
print(f"运行async main携程耗时:{time.time() - now}秒")